package org.example.filter;

import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.Optional;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * 获取请求以及响应数据
 * DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); +  gzip解码方可解决乱码情况
 */
@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public int getOrder() {
        return -2;
    }
 
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        ServerHttpResponse originalResponse = exchange.getResponse();

        String url = serverHttpRequest.getPath().toString();

        //请求参数，post从请求里获取请求体 HttpMethod.POST.equals(method) ?
        GatewayContext gatewayContext = GatewayContext.getContext(exchange);
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();

        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (serverHttpRequest instanceof MultipartHttpServletRequest) {
                    log.debug("请求 [{}] 为上传文件操作,关闭日志搜集",url);
                } else if (originalResponse.getHeaders().containsKey("Content-Disposition")) {
                    log.debug("请求 [{}] 为下载文件操作,关闭日志搜集,判断依据是响应中携带 Content-Disposition 请求头",url);
                } else if (body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                    return super.writeWith(fluxBody.buffer().map(dataBuffers -> {//解决返回体分段传输
                        DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
                        DataBuffer join = dataBufferFactory.join(dataBuffers);
                        byte[] content = new byte[join.readableByteCount()];
                        join.read(content);

                        byte[] uncompressContent = uncompress(content);
                        boolean isUncompress = true;
                        if (uncompressContent == null) {
                            uncompressContent = content;
                            isUncompress = false;
                        }
                        DataBufferUtils.release(join);
                        String responseData = new String(uncompressContent, StandardCharsets.UTF_8);

                        long resultLength;
                        if (isJson(responseData)) {
                            JSONObject jsonObject = JSONObject.parseObject(responseData);
                            JSONObject data = jsonObject.getJSONObject("data");
                            if (Objects.isNull(data)) {
                                data = new JSONObject();
                                jsonObject.put("data", data);
                            }
                            data.put("uuuuuuuuuuu", UUID.randomUUID().toString());
                            responseData = jsonObject.toJSONString();
                            log.info("time: {} ms, data: {}", System.currentTimeMillis() - gatewayContext.getStartTime(), JSON.toJSONString(gatewayContext));
                        }
                        //TODO，result就是response的值，想修改、查看就随意而为了
                        // log.debug("请求长度：" + StringUtils.length(gatewayContext.getCacheBody()) + "，返回data长度:" + resultLength);
                        // log.debug("\n请求地址:[{}] \n请求参数:GET [{}] \nPOST:[{}] \n响应数据:[{}]", url, JSON.toJSONString(gatewayContext.getUrlData()), gatewayContext.getCacheBody(), responseData);

                        byte[] uppedContent;
                        if (isUncompress) {
                            uppedContent = compress(responseData,"utf-8");
                        } else {
                            uppedContent = new String(responseData.getBytes(),StandardCharsets.UTF_8).getBytes();
                        }

                        originalResponse.getHeaders().setContentLength(uppedContent.length);
                        return bufferFactory.wrap(uppedContent);
                    }));
                }
                // if body is not a flux. never got there.
                return super.writeWith(body);
            }
        };
        // replace response with decorator
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }

    /**
     *
     * 解码 gzip,否则依旧会出现乱码情况
     * @param bytes
     * @return
     */
    public static byte[] uncompress(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        try {
            GZIPInputStream ungzip = new GZIPInputStream(in);
            byte[] buffer = new byte[256];
            int n;
            while ((n = ungzip.read(buffer)) >= 0) {
                out.write(buffer, 0, n);
            }
        } catch (Exception e) {
            log.debug("gzip uncompress error. {}", e.getMessage());
            return null;
        }

        return out.toByteArray();
    }

    /**
     * 编码 gzip
     * @param str
     * @param encoding
     * @return
     */
    public static byte[] compress(String str, String encoding) {
        if (str == null || str.length() == 0) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        GZIPOutputStream gzip;
        try {
            gzip = new GZIPOutputStream(out);
            gzip.write(str.getBytes(encoding));
            gzip.close();
        } catch (IOException e) {
            log.debug("gzip compress error.", e);
            return new String(str.getBytes(),StandardCharsets.UTF_8).getBytes();
        }
        return out.toByteArray();
    }

    /**
     * 判断是否为json
     * @param str 字符串
     * @return true 是json
     */
    public static boolean isJson(String str) {
        try {
            JSONObject.parseObject(str);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}